package com.zenchn.config;

import com.zenchn.exception.ShiroExceptionHandler;
import com.zenchn.shiro.MyFormAuthenticationFilter;
import com.zenchn.shiro.MySessionIdGenerator;
import com.zenchn.shiro.MySessionManager;
import com.zenchn.shiro.SecurityRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.servlet.Filter;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedHashMap;
import java.util.Map;


/**
 *Shiro 组件 注册配置
 * @author panbingqi
 * @date 2021-06-01
 */
@Configuration
public class ShiroConfig {

//    @Value("${spring.redis.host}")
//    private String redisHost;
//    @Value("${spring.redis.port}")
//    private String redisPort;
//    @Value("${spring.redis.timeout}")
//    private int redisTimeout;
//    @Value("${spring.redis.password}")
//    private String redisPassword;


//    @Value("${spring.shiro.sessionExpire}")
//    private int sessionExpire;
//
//    @Value("${spring.shiro.cacheExpire}")
//    private int cacheExpire;

//    @Value("${spring.shiro.sessionPrefix}")
//    private String sessionPrefix;

//    @Value("${spring.shiro.cachePrefix}")
//    private String cachePrefix;

    @Value("${custom.swagger.enable}")
    private boolean swaggerEnable;


    /**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * 注意：单独一个ShiroFilterFactoryBean配置是或报错的，以为在
     * 初始化ShiroFilterFactoryBean的时候需要注入：SecurityManager
     *
     * Filter Chain定义说明 1、一个URL可以配置多个Filter，使用逗号分隔 2、当设置多个过滤器时，全部验证通过，才视为通过
     * 3、部分过滤器可指定参数，如perms，roles
     *
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 获取filters
        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
        // 将自定义的FormAuthenticationFilter注入shiroFilter中
        filters.put("authc", new MyFormAuthenticationFilter());

        //拦截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        //配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");

        if (swaggerEnable) {
            //swagger 接口调试组件
            filterChainDefinitionMap.put("/swagger-ui.html", "anon");
            filterChainDefinitionMap.put("/swagger-resources/**", "anon");
            filterChainDefinitionMap.put("/webjars/springfox-swagger-ui/**", "anon");
            filterChainDefinitionMap.put("/v2/**", "anon");
            filterChainDefinitionMap.put("/csrf/**", "anon");


        }

        //druid 监测
        filterChainDefinitionMap.put("/druid/**", "anon");
        //未登录接口
        filterChainDefinitionMap.put("/error/unlogin", "anon");

        //配置登录 登出 不会被拦截的链接
        filterChainDefinitionMap.put("/login/**", "anon");
        //获取登录页相关信息 不会被拦截的链接
        filterChainDefinitionMap.put("/loginPage/**", "anon");

        //找回密码相关接口 不会被拦截的链接
        filterChainDefinitionMap.put("/retrievePwd/**", "anon");
        //回调接口 不会被拦截的链接
        filterChainDefinitionMap.put("/callback/**", "anon");


        //配置退出 过滤器,不用系统默认实现接口
        //filterChainDefinitionMap.put("/login/logout", "logout");

        //配置shiro默认登录界面地址，前后端分离中登录界面跳转应由前端路由控制，后台仅返回json数据
        shiroFilterFactoryBean.setLoginUrl("/error/unlogin");
        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/error/401");

        // 过滤链定义，从上向下顺序执行，一般将/**放在最为下边 :这是一个坑呢，一不小心代码就不好使了;
        //authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
        filterChainDefinitionMap.put("/**", "authc");


        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * @desc: 配置核心安全事务管理
     * @date: 2018-07-07
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 自定义session管理 使用redis
//        securityManager.setSessionManager(sessionManager());
        // 自定义缓存实现 使用redis
//        securityManager.setCacheManager(redisCacheManager());
        //注入记住我管理器
        securityManager.setRememberMeManager(rememberMeManager());
        securityManager.setRealm(securityRealm());
        return securityManager;
    }

    @Bean
    public SecurityRealm securityRealm(){
        return new SecurityRealm();
    }


//    /**
//     * 自定义sessionManager
//     */
//
//    @Bean
//    public SessionManager sessionManager() {
//        MySessionManager mySessionManager = new MySessionManager();
//
//        mySessionManager.setSessionDAO(redisSessionDAO());
//        return mySessionManager;
//
//    }

    @Bean
    public SimpleCookie rememberMeCookie(){
        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        // 记住我cookie生效时间30天 ,单位秒;
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }

    @Bean
    public CookieRememberMeManager rememberMeManager(){
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(generateCipherKey());
        return cookieRememberMeManager;
    }

//    /**
//     * 配置shiro redisManager
//     * 使用的是shiro-redis开源插件
//     * @return
//     */
//    @Bean
//    public RedisManager redisManager() {
//        RedisManager redisManager = new RedisManager();
//        redisManager.setHost(redisHost+":"+redisPort);
//        redisManager.setTimeout(redisTimeout);
//        redisManager.setPassword(redisPassword);
//        return redisManager;
//    }

//    /**
//     * cacheManager 缓存 redis实现
//     * 使用的是shiro-redis开源插件 principalIdFieldName
//     * @return
//     */
//    @Bean
//    public RedisCacheManager redisCacheManager() {
//       RedisCacheManager redisCacheManager = new RedisCacheManager();
//        redisCacheManager.setRedisManager(redisManager());
//        redisCacheManager.setExpire(cacheExpire);
//        redisCacheManager.setKeyPrefix(cachePrefix);
//        redisCacheManager.setPrincipalIdFieldName("account");
//
//
//
//        return redisCacheManager;
//    }

//    /**
//     * RedisSessionDAO shiro sessionDao层的实现 通过redis
//     * 使用的是shiro-redis开源插件
//     */
//    @Bean
//    public RedisSessionDAO redisSessionDAO() {
//        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
//        redisSessionDAO.setExpire(sessionExpire);
//        redisSessionDAO.setKeyPrefix(sessionPrefix);
//        redisSessionDAO.setRedisManager(redisManager());
//        redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
//        return redisSessionDAO;
//    }


    /**
     * 自定义sessionID 生成规则
     * @return
     */
    @Bean
    public MySessionIdGenerator sessionIdGenerator() {
        return new MySessionIdGenerator();
    }





    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


    /**
     * 注册全局异常处理
     * @return
     */
    @Bean(name = "exceptionHandler")
    public HandlerExceptionResolver handlerExceptionResolver() {
        return new ShiroExceptionHandler();
    }


    /**
     * 生成秘钥
     * @return
     */
    public  byte[] generateCipherKey() {
        KeyGenerator kg;
        try {
            kg = KeyGenerator.getInstance("AES");
        } catch (NoSuchAlgorithmException var5) {
            //这里的msg可以任意随机输入
            String msg = "market-information-management-api";
            throw new IllegalStateException(msg, var5);
        }

        kg.init(128);
        SecretKey key = kg.generateKey();
        byte[] encoded = key.getEncoded();
        return encoded;
    }
}